<?php

namespace Wonfu\Core\Route;

use Noodlehaus\Config;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use DI\Annotation\Inject;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Wonfu\Core\Annotations\RequestMapping;
use Wonfu\Core\Annotations\RequestTag;
use Wonfu\Core\AppConfig;
use Wonfu\Openapi\Info;
use Wonfu\Openapi\Server;
use Wonfu\Reflections\MethodWrapper;
use Wonfu\Reflections\Reflections;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Yaml\Yaml;
use Doctrine\Common\Annotations\AnnotationReader;

class RouteManager extends \Symfony\Component\Console\Command\Command
{
    // the name of the command (the part after "bin/console")
    protected static $defaultName = 'route:manager';

    /**
     * @Inject("appConfig")
     * @var AppConfig
     */
    private $appConfig;

    /**
     * @Inject
     * @var Config
     */
    private $envConfig;


    protected function configure()
    {
        $this->setDescription('Manage restful route.')
            ->addArgument('taskid')
            ->addOption('generate', null, InputOption::VALUE_OPTIONAL,
                'generate the openapi file', false)
            ->setHelp('This command allows you to show all the project restful route and generate a openapi config file');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $reflections = new Reflections();
        $table = new Table($output);
        $table
            ->setHeaders(['Category', 'Name', 'Path', 'Mapping', 'Method']);
        $methods = $reflections->include($this->appConfig->getControllerDir())
            ->finder()
            ->getMethodsWithAnnotation(RequestMapping::class, RequestTag::class);
        /**@var  MethodWrapper[] $methods */
        $rows = [];
        $count = count($methods);
        foreach ($methods as $method) {
            /** @var RequestMapping $requestMapping */
            $requestMapping = $method->getAnnotation();
            /** @var RequestTag $requestTag */
            $requestTag = $method->getClassAnnotation();
            $rows[] = [
                $requestTag ? $requestTag->name : '--',
                $requestMapping->name,
                $requestMapping->path,
                $method->getReflectionMethod()->class . "::" . $method->getReflectionMethod()->name,
                $requestMapping->method
            ];
        }
        $table->setRows($rows);
        $table->render();
        $output->writeln("There are {$count} routes in your project!");
        if ($input->getOption('generate') !== false) {
            $this->generate($methods);
            $output->writeln("openapi file generated!");
        }
        return Command::SUCCESS;
    }

    /**
     * @param MethodWrapper[] $methods
     */
    private function generate($methods)
    {
        $oaGenerator = new OpenapiGenerator($methods);
        $encoders = [new XmlEncoder(), new JsonEncoder()];
        $normalizer = new ObjectNormalizer();
        $normalizers = [$normalizer];
        $serializer = new Serializer($normalizers, $encoders);
        $info = $serializer->deserialize(json_encode($this->envConfig->get('info')), Info::class, 'json');
        $oaGenerator->setInfo($info);
        $envServers = $this->envConfig->get('servers');
        $servers = [];
        foreach ($envServers as $desc => $url) {
            $server = new Server();
            $server->url = $url;
            $server->description = $desc;
            $servers[] = $server;
        }
        $oaGenerator->setServers($servers);
        $openapi = $oaGenerator->organization();
        $openapiArr = $normalizer->normalize($openapi, 'json', [AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
        //reslove php array auto conversion
        $jsonStr = json_encode($openapiArr);
        $obj = json_decode($jsonStr);
        $yaml = Yaml::dump($obj, 10, 4, Yaml::DUMP_OBJECT_AS_MAP);
        file_put_contents($this->appConfig->getRootDir() . '/openapi.yaml', $yaml);
    }

}